Skip to content

feat: migrate grpc-middleware v1 chaining and grpc-prometheus to v2#23

Merged
ankurs merged 8 commits intomainfrom
feat/phase4-grpc-middleware-prometheus-migration
Mar 26, 2026
Merged

feat: migrate grpc-middleware v1 chaining and grpc-prometheus to v2#23
ankurs merged 8 commits intomainfrom
feat/phase4-grpc-middleware-prometheus-migration

Conversation

@ankurs
Copy link
Copy Markdown
Member

@ankurs ankurs commented Mar 25, 2026

Summary

  • Replace go-grpc-middleware v1 ChainUnaryServer/Client/StreamClient with local chain helpers
  • Remove grpc_ctxtags — trace ID already propagated via loggers.AddToLogContext
  • Update grpc_retry to go-grpc-middleware/v2/interceptors/retry
  • Migrate go-grpc-prometheus to go-grpc-middleware/providers/prometheus (instance-based API)
  • Add SetServerMetricsOptions and SetClientMetricsOptions for configurable gRPC Prometheus metrics
  • Prometheus metric names preserved (grpc_server_handled_total, grpc_server_handling_seconds, etc.)
  • go-grpc-middleware v1 retained solely for grpc_opentracing (future removal phase)

Test plan

  • make test — all tests pass
  • make lint — 0 issues, no vulnerabilities
  • make build — compiles cleanly
  • make doc — README regenerated

Summary by CodeRabbit

  • New Features

    • Added public configuration options to customize Prometheus metrics for client and server interceptors and a documented compile-time compatibility constant.
  • Documentation

    • README updated to document the new metrics configuration options and refreshed reference links.
  • Chores

    • Updated Prometheus/gRPC instrumentation dependencies to newer modules and versions.
  • Tests

    • Added unit tests validating interceptor chaining order and behavior.

…etheus to providers/prometheus

Replace go-grpc-middleware v1 ChainUnary/StreamServer/Client with local
chain helpers. Remove grpc_ctxtags (trace ID already propagated via
loggers). Update grpc_retry to v2 module path. Migrate go-grpc-prometheus
to go-grpc-middleware/providers/prometheus with instance-based metrics API.

New exported functions SetServerMetricsOptions and SetClientMetricsOptions
allow configuring gRPC Prometheus metrics with any grpcprom option during
initialization. Metric names are preserved (grpc_server_handled_total,
grpc_server_handling_seconds, etc.).

go-grpc-middleware v1 retained solely for grpc_opentracing (future removal).
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Added lazy-initialized Prometheus metrics builders with option setters, introduced internal interceptor chaining helpers for unary/stream client and server flows, swapped Prometheus provider dependency, simplified server error trace handling, and added unit tests for the chain helpers.

Changes

Cohort / File(s) Summary
Docs
README.md
Documented SupportPackageIsVersion1 constant and new SetClientMetricsOptions / SetServerMetricsOptions; updated function doc links/line references.
Dependency management
go.mod
Removed github.com/grpc-ecosystem/go-grpc-prometheus; added github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 and github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3; promoted github.com/prometheus/client_golang v1.23.2 to direct require and updated several direct/indirect versions.
Interceptors implementation
interceptors.go
Added const SupportPackageIsVersion1 = true; added SetClientMetricsOptions and SetServerMetricsOptions; introduced getClientMetrics/getServerMetrics with sync.Once and registerCollector handling; replaced external chain helpers with chainUnaryServer, chainUnaryClient, chainStreamClient; switched metric provider usage and retry import; simplified server error trace handling.
Unit tests
interceptors_test.go
Added TestChainUnaryServer, TestChainUnaryClient, TestChainStreamClient verifying interceptor chain ordering and behavior.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ClientChain as Client Interceptor Chain
  participant GRPC as gRPC Transport
  participant ServerChain as Server Interceptor Chain
  participant Server
  participant PromReg as Prometheus Registry

  Client->>ClientChain: start RPC
  ClientChain->>PromReg: ensure client metrics registered (getClientMetrics)
  ClientChain->>GRPC: invoke gRPC call
  GRPC->>ServerChain: deliver request
  ServerChain->>PromReg: ensure server metrics registered (getServerMetrics)
  ServerChain->>Server: call handler
  Server->>ServerChain: return response / error (trace id attached)
  ServerChain->>GRPC: return response
  GRPC->>ClientChain: deliver response
  ClientChain->>Client: return response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰
I hop through code and mend the seams,
Metrics sprout from midnight dreams,
Chains step four in tidy rows,
Trace-id twinkles, onward goes,
A carrot clap for tests that beam!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 63.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main objective of the PR: migrating from gRPC middleware v1 chaining and grpc-prometheus to v2 versions, which aligns with the substantial changes across interceptors.go, go.mod, and documentation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/phase4-grpc-middleware-prometheus-migration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ankurs ankurs requested a review from Copilot March 25, 2026 10:29
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@interceptors.go`:
- Around line 531-536: The stream interceptor enriches ctx via
notifier.SetTraceId and loggers.AddToLogContext but still passes the original
grpc.ServerStream to handler, so downstream callers see the old context; wrap
the incoming stream in a small wrapper type (e.g. serverStreamWithContext
implementing grpc.ServerStream and overriding Context() to return the enriched
ctx) and pass that wrapped stream to handler instead of the original stream so
stream.Context() returns the new trace/log context; add the
serverStreamWithContext type and use it at the call site where handler(srv,
stream) is invoked.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c048cf28-a9d9-45aa-81d8-80e15744c404

📥 Commits

Reviewing files that changed from the base of the PR and between 610274d and c1cd48e.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (3)
  • README.md
  • go.mod
  • interceptors.go

Comment thread interceptors.go
Comment thread interceptors.go
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the interceptor stack off deprecated go-grpc-middleware v1 chaining and go-grpc-prometheus, moving to go-grpc-middleware/v2 retry and the instance-based Prometheus provider while keeping existing metric names.

Changes:

  • Replaced v1 Chain* helpers with local chaining helpers for unary server/client and stream client interceptors.
  • Removed grpc_ctxtags usage and switched trace ID logging to loggers.AddToLogContext.
  • Migrated Prometheus instrumentation to go-grpc-middleware/providers/prometheus and added metrics option setters for server/client.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 4 comments.

File Description
interceptors.go Swaps chaining implementation, removes ctxtags, introduces singleton Prometheus metrics instances with configurable options.
go.mod Replaces go-grpc-prometheus with providers/prometheus and adds go-grpc-middleware/v2.
go.sum Updates dependency checksums for the new Prometheus provider and middleware v2.
README.md Regenerated docs to reflect new/shifted APIs and line references.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread interceptors.go
Comment thread interceptors.go
Comment thread interceptors.go
Comment thread interceptors.go
… add chain tests

- Register ServerMetrics/ClientMetrics with prometheus.DefaultRegisterer
  inside getServerMetrics/getClientMetrics (non-panicking Register with
  warning log on duplicate). Without registration, metrics from
  providers/prometheus are collected but never scraped.
- Replace closure-per-RPC chain helpers with index-based runners that
  avoid per-call allocations.
- Add TestChainUnaryServer, TestChainUnaryClient, TestChainStreamClient
  to verify interceptor execution ordering.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
interceptors.go (1)

542-559: ⚠️ Potential issue | 🟠 Major

Stream context enrichment not propagated to handler.

The ctx is enriched with trace ID at lines 545-547, but handler(srv, stream) at line 549 receives the original stream whose Context() method returns the unenriched context. Downstream handlers reading stream.Context() will not see the trace ID.

This was flagged in a previous review. The fix requires wrapping the stream:

Proposed fix
+type serverStreamWithContext struct {
+	grpc.ServerStream
+	ctx context.Context
+}
+
+func (s *serverStreamWithContext) Context() context.Context {
+	return s.ctx
+}

Then at the call site:

-		err = handler(srv, stream)
+		err = handler(srv, &serverStreamWithContext{ServerStream: stream, ctx: ctx})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@interceptors.go` around lines 542 - 559, The handler is called with the
original grpc.ServerStream so its Context() doesn't include the added trace
info; wrap the stream with a small type (e.g., streamWithContext or
wrappedStream) that embeds grpc.ServerStream and overrides Context() to return
the enriched ctx (the ctx produced after notifier.SetTraceId and
loggers.AddToLogContext) and pass that wrapped stream into handler(srv,
wrappedStream) inside ServerErrorStreamInterceptor so downstream handlers
reading stream.Context() see the trace id.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@interceptors.go`:
- Around line 542-559: The handler is called with the original grpc.ServerStream
so its Context() doesn't include the added trace info; wrap the stream with a
small type (e.g., streamWithContext or wrappedStream) that embeds
grpc.ServerStream and overrides Context() to return the enriched ctx (the ctx
produced after notifier.SetTraceId and loggers.AddToLogContext) and pass that
wrapped stream into handler(srv, wrappedStream) inside
ServerErrorStreamInterceptor so downstream handlers reading stream.Context() see
the trace id.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 10f55110-6c2a-429b-a4fc-25f7694e2648

📥 Commits

Reviewing files that changed from the base of the PR and between c1cd48e and bfe96e2.

📒 Files selected for processing (4)
  • README.md
  • go.mod
  • interceptors.go
  • interceptors_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • README.md
  • go.mod

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 5 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread interceptors.go
Comment thread interceptors.go Outdated
Comment thread interceptors.go
When consumers update interceptors without updating core, the old
go-grpc-prometheus package's init() auto-registers metrics with the same
names. Our prometheus.Register() then fails with AlreadyRegisteredError,
leaving our metrics instance unregistered and unscraped.

Fix: on AlreadyRegisteredError, unregister the old collector and
re-register ours so the interceptors' metrics instance is the one being
scraped.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@interceptors.go`:
- Around line 127-128: The doc comment for SetClientMetricsOptions is incorrect
about lifecycle; update it to state that SetClientMetricsOptions configures
client-side interceptors and must be called during initialization before any
RPCs are made (not "before the server starts"), aligning its lifecycle guidance
with other client APIs; reference the SetClientMetricsOptions function and
mention it is not safe for concurrent use and should be invoked prior to
creating/using any gRPC clients.
- Around line 165-214: The three chain helpers (chainUnaryServer,
chainUnaryClient, chainStreamClient) use a shared mutable index `i` which breaks
re-entrancy when an interceptor calls next() multiple times; replace the current
incremental-i pattern with an index-capturing composition by building the
chained handler ahead of time (or using a recursive closure that captures the
current index) so each nested next closure closes over its own index instead of
a shared `i`; update chainUnaryServer, chainUnaryClient and chainStreamClient to
construct the composed handler/streamer/invoker by iterating from the end of the
interceptors slice (or via a recursive function param like idx) so retried calls
will run the full interceptor chain every time.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 715dcc12-d647-458a-93e3-f6ce61b33aa8

📥 Commits

Reviewing files that changed from the base of the PR and between bfe96e2 and 7901b4d.

📒 Files selected for processing (1)
  • interceptors.go

Comment thread interceptors.go
Comment on lines +127 to +128
// SetClientMetricsOptions appends gRPC client metrics options.
// Must be called during initialization, before the server starts. Not safe for concurrent use.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix the client metrics lifecycle docs.

SetClientMetricsOptions configures client-side interceptors, so “before the server starts” is the wrong lifecycle boundary. Please align this with the other client APIs and document that it must run before any RPCs are made.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@interceptors.go` around lines 127 - 128, The doc comment for
SetClientMetricsOptions is incorrect about lifecycle; update it to state that
SetClientMetricsOptions configures client-side interceptors and must be called
during initialization before any RPCs are made (not "before the server starts"),
aligning its lifecycle guidance with other client APIs; reference the
SetClientMetricsOptions function and mention it is not safe for concurrent use
and should be invoked prior to creating/using any gRPC clients.

Comment thread interceptors.go
…ict detection

Add compile-time version compatibility sentinel following the gRPC
SupportPackageIsVersion pattern. Downstream packages (core) reference
this constant to enforce exact version compatibility.

Update registerCollector to use errors.As for AlreadyRegisteredError
detection. When descriptor conflicts occur (e.g. go-grpc-prometheus
init() registered same metric names), log a clear error directing
users to update core.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
interceptors.go (2)

169-218: ⚠️ Potential issue | 🟠 Major

Make chain helpers re-entrant (shared index currently breaks repeated next calls).

All three helpers mutate a shared i. If any interceptor invokes next more than once, subsequent invocations skip part of the chain.

Proposed fix (index-capturing recursion)
 func chainUnaryServer(interceptors []grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor {
 	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
-		var i int
-		var next grpc.UnaryHandler
-		next = func(ctx context.Context, req interface{}) (interface{}, error) {
-			if i == len(interceptors) {
-				return handler(ctx, req)
-			}
-			interceptor := interceptors[i]
-			i++
-			return interceptor(ctx, req, info, next)
-		}
-		return next(ctx, req)
+		var call func(int, context.Context, interface{}) (interface{}, error)
+		call = func(i int, ctx context.Context, req interface{}) (interface{}, error) {
+			if i == len(interceptors) {
+				return handler(ctx, req)
+			}
+			return interceptors[i](ctx, req, info, func(nextCtx context.Context, nextReq interface{}) (interface{}, error) {
+				return call(i+1, nextCtx, nextReq)
+			})
+		}
+		return call(0, ctx, req)
 	}
 }

 func chainUnaryClient(interceptors []grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor {
 	return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
-		var i int
-		var next grpc.UnaryInvoker
-		next = func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, opts ...grpc.CallOption) error {
-			if i == len(interceptors) {
-				return invoker(ctx, method, req, reply, cc, opts...)
-			}
-			interceptor := interceptors[i]
-			i++
-			return interceptor(ctx, method, req, reply, cc, next, opts...)
-		}
-		return next(ctx, method, req, reply, cc, opts...)
+		var call func(int, context.Context, string, interface{}, interface{}, *grpc.ClientConn, ...grpc.CallOption) error
+		call = func(i int, ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, opts ...grpc.CallOption) error {
+			if i == len(interceptors) {
+				return invoker(ctx, method, req, reply, cc, opts...)
+			}
+			return interceptors[i](ctx, method, req, reply, cc, func(nextCtx context.Context, nextMethod string, nextReq, nextReply interface{}, nextCC *grpc.ClientConn, nextOpts ...grpc.CallOption) error {
+				return call(i+1, nextCtx, nextMethod, nextReq, nextReply, nextCC, nextOpts...)
+			}, opts...)
+		}
+		return call(0, ctx, method, req, reply, cc, opts...)
 	}
 }

 func chainStreamClient(interceptors []grpc.StreamClientInterceptor) grpc.StreamClientInterceptor {
 	return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
-		var i int
-		var next grpc.Streamer
-		next = func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) {
-			if i == len(interceptors) {
-				return streamer(ctx, desc, cc, method, opts...)
-			}
-			interceptor := interceptors[i]
-			i++
-			return interceptor(ctx, desc, cc, method, next, opts...)
-		}
-		return next(ctx, desc, cc, method, opts...)
+		var call func(int, context.Context, *grpc.StreamDesc, *grpc.ClientConn, string, ...grpc.CallOption) (grpc.ClientStream, error)
+		call = func(i int, ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) {
+			if i == len(interceptors) {
+				return streamer(ctx, desc, cc, method, opts...)
+			}
+			return interceptors[i](ctx, desc, cc, method, func(nextCtx context.Context, nextDesc *grpc.StreamDesc, nextCC *grpc.ClientConn, nextMethod string, nextOpts ...grpc.CallOption) (grpc.ClientStream, error) {
+				return call(i+1, nextCtx, nextDesc, nextCC, nextMethod, nextOpts...)
+			}, opts...)
+		}
+		return call(0, ctx, desc, cc, method, opts...)
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@interceptors.go` around lines 169 - 218, The chain helpers (chainUnaryServer,
chainUnaryClient, chainStreamClient) use a shared mutable index `i`, so if any
interceptor calls `next` more than once the second call skips interceptors; fix
by making the recursion index-local (capture the index per invocation) — replace
the shared `i`/single `next` pattern with a recursive function that takes an
index parameter (e.g. chain(idx int) or nextAt(index int)) and invokes the
appropriate interceptor or final handler/invoker/streamer using that index,
incrementing the index only for the recursive call; apply the same
index-capturing recursion change to the UnaryServer, UnaryClient (use invoker),
and StreamClient (use streamer) helpers so repeated `next` calls traverse the
chain correctly.

133-135: ⚠️ Potential issue | 🟡 Minor

Correct SetClientMetricsOptions lifecycle docs.

This is a client-side configuration API, so “before the server starts” is the wrong boundary. It should say it must be called during init before any RPCs are made.

Proposed doc fix
 // SetClientMetricsOptions appends gRPC client metrics options.
-// Must be called during initialization, before the server starts. Not safe for concurrent use.
+// Must be called during initialization, before any RPCs are made. Not safe for concurrent use.
 func SetClientMetricsOptions(opts ...grpcprom.ClientMetricsOption) {
 	cltMetricsOpts = append(cltMetricsOpts, opts...)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@interceptors.go` around lines 133 - 135, Update the doc comment for
SetClientMetricsOptions to reflect its client-side lifecycle: replace “Must be
called during initialization, before the server starts. Not safe for concurrent
use.” with a line stating it must be called during initialization before any
RPCs are made and is not safe for concurrent use, so callers should configure it
prior to making client calls; reference the function name
SetClientMetricsOptions in the comment so it's clear which API is affected.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@interceptors.go`:
- Around line 149-150: The current log.Error call in interceptors.go (the
log.Error(..., "gRPC Prometheus metrics will not be available: the deprecated
go-grpc-prometheus package has already registered metrics with the same names.
Update github.com/go-coldbrew/core to the latest version to fix this.", "err",
err)) is misleading for non-AlreadyRegistered errors; update that message to a
neutral, accurate statement (e.g., "gRPC Prometheus metrics registration
failed") and include the actual error details (err) in the log so callers see
the real cause instead of incorrectly blaming the deprecated package. Ensure the
change is applied to the log.Error invocation that uses the err variable so
debugging shows the error text.

---

Duplicate comments:
In `@interceptors.go`:
- Around line 169-218: The chain helpers (chainUnaryServer, chainUnaryClient,
chainStreamClient) use a shared mutable index `i`, so if any interceptor calls
`next` more than once the second call skips interceptors; fix by making the
recursion index-local (capture the index per invocation) — replace the shared
`i`/single `next` pattern with a recursive function that takes an index
parameter (e.g. chain(idx int) or nextAt(index int)) and invokes the appropriate
interceptor or final handler/invoker/streamer using that index, incrementing the
index only for the recursive call; apply the same index-capturing recursion
change to the UnaryServer, UnaryClient (use invoker), and StreamClient (use
streamer) helpers so repeated `next` calls traverse the chain correctly.
- Around line 133-135: Update the doc comment for SetClientMetricsOptions to
reflect its client-side lifecycle: replace “Must be called during
initialization, before the server starts. Not safe for concurrent use.” with a
line stating it must be called during initialization before any RPCs are made
and is not safe for concurrent use, so callers should configure it prior to
making client calls; reference the function name SetClientMetricsOptions in the
comment so it's clear which API is affected.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7a6d5ac3-35db-49f2-a958-38a149bb2f60

📥 Commits

Reviewing files that changed from the base of the PR and between 7901b4d and 111f67f.

📒 Files selected for processing (2)
  • README.md
  • interceptors.go

Comment thread interceptors.go Outdated
ankurs added 2 commits March 26, 2026 09:24
Reference upstream errors package sentinel to enforce version
compatibility at compile time.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
go.mod (1)

187-187: Move gomarkdoc from indirect to direct tooling requirement.

gomarkdoc is explicitly listed in the tool (...) block (lines 278–282), making it a direct tooling dependency. Remove the // indirect marker on line 187 to follow the Go convention that explicitly required tools remain as direct requires.

Proposed go.mod adjustment
-	github.com/princjef/gomarkdoc v1.1.0 // indirect
+	github.com/princjef/gomarkdoc v1.1.0
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@go.mod` at line 187, The go.mod currently marks github.com/princjef/gomarkdoc
as an indirect dependency; update the requirement so gomarkdoc is a direct
tooling requirement by removing the "// indirect" marker for
github.com/princjef/gomarkdoc v1.1.0 and ensuring it remains declared in the
tool (...) block; edit the go.mod entry for the module name
"github.com/princjef/gomarkdoc" to remove the indirect comment so it is treated
as a direct tooling requirement.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@go.mod`:
- Line 187: The go.mod currently marks github.com/princjef/gomarkdoc as an
indirect dependency; update the requirement so gomarkdoc is a direct tooling
requirement by removing the "// indirect" marker for
github.com/princjef/gomarkdoc v1.1.0 and ensuring it remains declared in the
tool (...) block; edit the go.mod entry for the module name
"github.com/princjef/gomarkdoc" to remove the indirect comment so it is treated
as a direct tooling requirement.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a88d003b-cd14-46d0-b372-37f70ee441b1

📥 Commits

Reviewing files that changed from the base of the PR and between 111f67f and 80cdf42.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (2)
  • go.mod
  • interceptors.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • interceptors.go

ankurs added 2 commits March 26, 2026 10:59
Replace shared mutable index with backwards iteration where each
closure captures its own interceptor and next handler. Safe for
concurrent use and if an interceptor calls next() multiple times.

Add concurrent tests for all three chain helpers (server, client,
stream client) that validate output correctness across 100 goroutines
with -race enabled.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 5 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread interceptors.go
Comment thread interceptors.go
Comment thread interceptors.go
Comment thread interceptors.go
@ankurs ankurs merged commit a7f0228 into main Mar 26, 2026
12 checks passed
@ankurs ankurs deleted the feat/phase4-grpc-middleware-prometheus-migration branch March 26, 2026 03:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants